﻿#ifndef __AVOID_CONTROL_EDIT_HEAD_FILE_INCLUDED__
#define __AVOID_CONTROL_EDIT_HEAD_FILE_INCLUDED__
#include "control.h"
#include "ctlScroll.h"
#include "interface/IFont.h"
#include "interface/IBackground.h"

namespace swui
{
#pragma warning(push)
#pragma warning(disable:4250)
	class ctlEdit : virtual public ctlRoot, 
		public IFont, public IBackground
	{
	public:
		static const ctlControlType uniqueType = ctlControlType::ctlEdit;
	public:
		virtual ~ctlEdit() { destroy(); }
		bool create(ctlRoot* parent, ctlRect rect, const ctlText& lpszText = ctlText(L""),
			const ctlStyleEdit& = ctlStyleEdit(ctlStyleEdit::common));
	public:
		//获取被选中的字符串
		std::string getSelectedString(std::string::const_pointer lineDelim = "\n");
		std::wstring getSelectedWideString(std::wstring::const_pointer lineDelim = L"\n");
		//获取指定段落的文本（从 0 开始）
		std::string getParagraph(unsigned number);
		std::wstring getWideParagraph(unsigned number);
		//设置指定段的内容（无访问限制）
		bool setParagraph(unsigned number, const ctlText& text);
		//插入段到指定位置
		bool insertParagraph(unsigned number, const ctlText& text);
		//移除段
		bool removeParagraph(unsigned number);
	public:
		//设置行间距（可以为负数）
		void setLineLength(int length);
		int getLineLength();
		//获得全部的行数
		unsigned getLineNumber();
		//获取指定段的行数
		unsigned getParagraphLineNumber(unsigned);
		//获取段落总数
		unsigned getParagraphNumber();
		//获得全部文本需要的高度
		unsigned getTotalHeight();
		//获得内边距
		unsigned getRightPadding();
		unsigned getLeftPadding();
		unsigned getHeightPadding();
		//获取滚动条
		ctlScroll* getScroll();
	public:
		void setAccess(ctlAccess access);
		ctlAccess getAccess();
		bool accessWrite();
		bool accessRead();
	public:
		virtual bool setText(const ctlText& lpszText);
		virtual unsigned getTextLength();
		virtual unsigned getANSITextSize();
		virtual unsigned getText(char* lpszText, unsigned bufferLen = 0);
		virtual unsigned getText(std::wstring::pointer lpszText, unsigned bufferLen = 0);
		virtual std::string getText();
		virtual std::wstring getWideText();
	private:
		typedef std::pair<unsigned, unsigned> _edit_pos;
	private:
		virtual void _redraw_(_ctlInterior::drawInfo*);
		virtual void _mouse_event_(ctlMouseEvent*);
		virtual void _key_event_(ctlKeyEvent*);
		virtual void _position_event_(ctlPositionEvent*);
	private:
		struct _textInfo
		{
			// 以段为基础
			_ctlInterior::textpack str;
			std::vector<_edit_pos> wrap;
		};
		struct _exactPos 
		{
			_exactPos(unsigned sec = 0, unsigned lin = 0, unsigned off = 0)
				: section(sec), line(lin), offset(off) {}
			unsigned section, line, offset; 
		};
		std::vector<_textInfo> _edit_str;
		_exactPos _edit_caret_pos_begin, _edit_caret_pos_end;
		_edit_pos _edit_top_pos;	//当前可见的第一行
		unsigned _edit_top_offset;	//第一行的可见长度
		int _edit_line_length;
		ctlScroll _edit_scroll;
		ctlAccess _edit_access;
		bool _edit_scroll_is_callback;
	private:
		struct {
			_ctlInterior::textpack pack;
			bool enable;
			_exactPos begin, end;
		} _edit_back_insert, _edit_back_remove;
	private:
		_textInfo* _getSection(unsigned section);
		std::vector<_edit_pos>* _getLineInfo(unsigned section);
		unsigned _getLineNumber(unsigned section);
		void _calc_text_rect(_edit_pos begin);
		_exactPos _edit_map_point_to_pos(ctlPoint);
		ctlPoint _edit_map_pos_to_point(_exactPos);
		_exactPos _insert_text(_exactPos pos, const _ctlInterior::textpack& text);
		_exactPos _get_next_pos(_exactPos);
		_exactPos _get_previous_pos(_exactPos);
		void _remove_selected();
		void _send_change_event();
		bool _send_edit_event(std::wstring& str, decltype(ctlNotifyEdit::copy));
		void _flush_caret();
		virtual void _flush_font_();
		void _caret_move(int distance, bool isAllChange);
		void _check_pos(_exactPos&);
		void _check_scroll();
		void _check_scrollbar();
		void _edit_rect_recalc();
		void _draw_selected_text(_ctlInterior::drawInfo*, int);
		unsigned _get_abs_line(_edit_pos);
		_edit_pos _get_pos_by_abs_pixel(unsigned);
		unsigned _get_abs_pixel_by_pos(_edit_pos);
		_exactPos _get_exact_pos_by_section_ans_offset(unsigned section, unsigned offset);
	};
#pragma warning(pop)

	inline void ctlEdit::setAccess(ctlAccess access) {
		_edit_access = access;
		if(access == ctlAccess::read || access == ctlAccess::none)
			_edit_caret_pos_end = _edit_caret_pos_begin;
		_flush_caret();
	}
	inline ctlAccess ctlEdit::getAccess() {
		return _edit_access;
	}
	inline bool ctlEdit::accessWrite() {
		return enumTypeAnd(getAccess(), ctlAccess::write) != ctlAccess::none;
	}
	inline bool ctlEdit::accessRead() {
		return enumTypeAnd(getAccess(), ctlAccess::read) != ctlAccess::none;
	}
	inline unsigned ctlEdit::getLeftPadding() {
		return 3;
	}
	inline unsigned ctlEdit::getRightPadding() {
		unsigned padding = 3;
		if(getScroll()->isShow())
			padding += getScroll()->getRealScrollSize();
		return padding;
	}
	inline unsigned ctlEdit::getHeightPadding() {
		return 3;
	}
	inline int ctlEdit::getLineLength() {
		return _edit_line_length;
	}
	inline void ctlEdit::setLineLength(int length) {
		_edit_line_length = length;
		redraw();
	}
	inline unsigned ctlEdit::getTotalHeight() {
		unsigned line = getLineNumber();
		if(line == 0) return 0;
		return line * getFont()->getFontHeight() +
			(line - 1) * getLineLength();
	}
	inline ctlScroll* ctlEdit::getScroll() {
		return &_edit_scroll;
	}
}

#endif